package com.rengwuxian.coursecoroutines._4_flow

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext

/**
 * map()系列操作符
 * map() / mapNotNull() / mapLatest()
 *
 * 1.map{} : 提供一个算法，可以把上游过来的数据转换成另一个数据来发送到下游去。
 * 2.mapNotNull{} : 先执行map{}的逻辑，然后把转换过后值为null的元素过滤掉。
 *     等价效果就是：先map{}再filterNotNull()
 *     或者：filter{}.map{}
 * 3.mapLatest{} : 是一个异步计算的map，Flow的数据流都是线性的，或者说是同步的，
 *   当map()在处理上游过来的一条数据的过程中，它是不会开始生产下一条上游数据的，直到这条处理完了，
 *   往下游发送了，上游的下一条数据才会开始生产，而mapLatest()没有这种限制，它是异步的，在处理上一条数据的过程中，
 *   上游依然可以生产下一条数据，并且下一条处理数据到来了，它会直接开始处理这条上游的新数据。
 *   是使用Channel做到的。
 *   只关注最新的数据，取消正在处理的旧数据。
 *   新的可以打断旧的业务逻辑，所以mapLatest()实际上是并列的、异步的生产流程。不是Flow正常情况下，线性同步的流程。
 *   异步和并列并不是这个操作符本身的含义，关键是利用这种机制实现了新数据对旧数据的生产流程的打断。
 *   适用于：数据转换比较慢的场景，一旦新数据到来，旧数据就没有意义了。
 *   比如：搜索提示网络请求已经发出后，用户又输入新的了，那么之前的网络请求就无用了。
 * 4.输出：
 *   1: null
 *   1: 5
 *   1: 6
 *   2: 2
 *   2: 3
 *   2: 5
 *   2: 6
 *   3: 4
 */
fun main() = runBlocking<Unit> {
  val scope = CoroutineScope(EmptyCoroutineContext)
  val flow1 = flowOf(1, 2, 3, 4, 5)
  val flow2 = flow {
    delay(100)
    emit(1)
    delay(100)
    emit(2)
    delay(100)
    emit(3)
  }
  scope.launch {
    flow1.map { if (it == 3) null else it + 1 }.collect { println("1: $it") }
    flow1.mapNotNull { if (it == 3) null else it + 1 }.collect { println("2: $it") }
    flow1.map { if (it == 3) null else it + 1 }.filterNotNull()
    flow1.filter { it != 3 }.map { it + 1 }
    flow2.mapLatest { delay(120); it + 1}.collect { println("3: $it") }
  }
  delay(10000)
}